using System;
using System.Collections.Generic;
using Firefly.ExtensionMethods;
using Firefly.Framework;
using Firefly.Framework.Components;
using Firefly.Framework.Components.Cameras;
using Firefly.Framework.Components.Sky;
using Firefly.Framework.Components.Terrain;
using Firefly.Framework.Graphics;
using Firefly.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace SkyDomeExample
{
    public class SkyDomeTest : Engine
    {
        private int _wavelengthIndex;
        private Vector3[] _wavelengths = 
        { 
            new Vector3(0.65f, 0.57f, 0.475f),
            new Vector3(0.65f, 1.57f, 2.475f),
            new Vector3(2.65f, 0.57f, 2.475f) 
        };
        private QuadTerrain _terrain;
        private ICamera _camera;
        private FillMode _fillMode;
        private SkyDome _skyDome;
        private TextCompositor _cameraPosition;
        private FrameRateCounter _frameRate;
        private TextCompositor _theta;
        private TextCompositor _cameraKey;
        private TextCompositor _skyDomeKey;
        private TextCompositor _engineKey;

        public SkyDomeTest()
        {
            ScreenWidth = 1024;
            ScreenHeight = 800;
        }

        protected override void Initialize()
        {
            InputService.Instance.MouseManager.ResetAfterUpdate = true;
            base.Initialize();
        }

        protected override void LoadContent()
        {
            base.LoadContent();
            InitializeCamera();
            InitializeSkyDome();
            InitializeTerrain();
            _fillMode = FillMode.Solid;
            _cameraPosition = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 20f), Color = Color.GreenYellow };
            _cameraPosition.LoadContent();
            _frameRate = new FrameRateCounter(this, "Fonts\\Kootenay") { Position = new Vector2(5f, 5f), Color = Color.GreenYellow };
            _frameRate.LoadContent();
            _theta = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 35f), Color = Color.GreenYellow };
            _theta.LoadContent();
            _cameraKey = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 785f), Color = Color.GreenYellow };
            _cameraKey.LoadContent();
            _cameraKey.Text = "[Camera] Forward : W | Left : A | Right : D | Backward : S";
            _skyDomeKey = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 770f), Color = Color.GreenYellow };
            _skyDomeKey.LoadContent();
            _skyDomeKey.Text = "[SkyDome] Increase Theta : + | Decrease Theta : - | Change Wavelength : \\ | Increase Cloudcover : . | Decrease Cloudcover : ,";
            _engineKey = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 755f), Color = Color.GreenYellow };
            _engineKey.LoadContent();
            _engineKey.Text = "[Engine] Exit : Esc | Toggle FillMode : Tab";
        }

        protected override void UnloadContent()
        {
            _skyDome.Dispose();
            _terrain.Dispose();
            _cameraPosition.Dispose();
            _frameRate.Dispose();
            _theta.Dispose();
            _cameraKey.Dispose();
            _skyDomeKey.Dispose();
            _engineKey.Dispose();
        }

        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            _camera.Update(gameTime);
            _skyDome.Update(gameTime);
            _terrain.Shader.SunlightDirection = _skyDome.SunlightDirection;
            _terrain.Shader.SunlightColor = _skyDome.SunlightColor.ToVector3();
            _terrain.Update(gameTime);
            _cameraPosition.Text = string.Format("Camera Position: {0}", _camera.Position);
            _theta.Text = string.Format("SkyDome Theta: {0}", _skyDome.Theta);
            _frameRate.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsService.Instance.Clear(Color.CornflowerBlue);
            GraphicsService.Instance.FillMode = _fillMode;
            _skyDome.Draw(gameTime);
            _terrain.Draw(gameTime);
            GraphicsService.Instance.FillMode = FillMode.Solid;
            _cameraPosition.Draw(gameTime);
            _theta.Draw(gameTime);
            _frameRate.Draw(gameTime);
            _cameraKey.Draw(gameTime);
            _skyDomeKey.Draw(gameTime);
            _engineKey.Draw(gameTime);
            base.Draw(gameTime);
        }

        private void InitializeCamera()
        {
            _camera = new FreeCamera(this)
            {
                Position = new Vector3(1480, 170, 1260),
                Pitch = (float)-(Math.PI / 2)
            };
        }

        private void InitializeSkyDome()
        {
            _skyDome = new SkyDome(this, _camera) { DrawOrder = -1 };
            _skyDome.LoadContent();
        }

        private void InitializeTerrain()
        {
            _terrain = new QuadTerrain(this, "\\Textures\\Terrain\\HeightMap2", 17, 257, 10, 0.5f, _camera) { DrawOrder = 0, LevelOfDetail = 3.5f };
            _terrain.LoadContent();
            _terrain.Shader.SunlightDirection = _skyDome.SunlightDirection;
            _terrain.Shader.SunlightColor = _skyDome.SunlightColor.ToVector3();
        }

        #region IInputProcessor Members

        public override void InitializeInputMaps()
        {
            HoldThreshold = 100;
            KeyMaps = new List<KeyMap>();
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.Escape }));
            KeyMaps[0].MapPressed += new EventHandler<KeyMapEventArgs>(OnExit);
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.Tab }));
            KeyMaps[1].MapPressed += new EventHandler<KeyMapEventArgs>(OnToggleFillMode);
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.OemPlus }));
            KeyMaps[2].MapPressed += new EventHandler<KeyMapEventArgs>(OnIncreaseTheta);
            KeyMaps[2].MapHeld += new EventHandler<KeyMapEventArgs>(OnIncreaseTheta);
            KeyMaps[2].HoldThreshold = HoldThreshold;
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.OemMinus }));
            KeyMaps[3].MapPressed += new EventHandler<KeyMapEventArgs>(OnDecreaseTheta);
            KeyMaps[3].MapHeld += new EventHandler<KeyMapEventArgs>(OnDecreaseTheta);
            KeyMaps[3].HoldThreshold = HoldThreshold;
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.OemPipe }));
            KeyMaps[4].MapPressed += new EventHandler<KeyMapEventArgs>(OnChangeWavelength);
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.OemComma }));
            KeyMaps[5].MapPressed += new EventHandler<KeyMapEventArgs>(OnDecreaseCloudCover);
            KeyMaps[5].MapHeld += new EventHandler<KeyMapEventArgs>(OnDecreaseCloudCover);
            KeyMaps[5].HoldThreshold = HoldThreshold;
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.OemPeriod }));
            KeyMaps[6].MapPressed += new EventHandler<KeyMapEventArgs>(OnIncreaseCloudCover);
            KeyMaps[6].MapHeld += new EventHandler<KeyMapEventArgs>(OnIncreaseCloudCover);
            KeyMaps[6].HoldThreshold = HoldThreshold;
        }

        private void OnIncreaseCloudCover(object sender, KeyMapEventArgs e)
        {
            _skyDome.PerlinClouds.CloudCover += 0.1f;
        }

        private void OnDecreaseCloudCover(object sender, KeyMapEventArgs e)
        {
            _skyDome.PerlinClouds.CloudCover -= 0.1f;
        }

        private void OnChangeWavelength(object sender, KeyMapEventArgs e)
        {
            _wavelengthIndex++;

            if (_wavelengthIndex >= _wavelengths.Length)
            {
                _wavelengthIndex = 0;
            }

            _skyDome.Shader.Wavelength = _wavelengths[_wavelengthIndex];
        }
        
        private void OnIncreaseTheta(object sender, KeyMapEventArgs e)
        {
            _skyDome.Theta += 0.1f;

            if (_skyDome.Theta > (float)(Math.PI * 2))
            {
                _skyDome.Theta = 0;
            }
        }

        private void OnDecreaseTheta(object sender, KeyMapEventArgs e)
        {
            _skyDome.Theta -= 0.1f;

            if (_skyDome.Theta < 0)
            {
                _skyDome.Theta = (float)(Math.PI * 2);
            }
        }

        private void OnToggleFillMode(object sender, KeyMapEventArgs e)
        {
            if (_fillMode == FillMode.WireFrame)
            {
                _fillMode = FillMode.Solid;
            }
            else
            {
                if (_fillMode == FillMode.Solid)
                {
                    _fillMode = FillMode.Point;
                }
                else
                {
                    if (_fillMode == FillMode.Point)
                    {
                        _fillMode = FillMode.WireFrame;
                    }
                }
            }
        }

        private void OnExit(object sender, KeyMapEventArgs e)
        {
            Exit();
        }

        #endregion
    }
}
